﻿namespace Snipps.ViewModels.Filters
{
    using System;
    using System.Linq;
    using System.Windows.Threading;

    using Snipps.Core;
    using Snipps.Infrastructure;

    public abstract class FilterViewModelBase : ValidatableViewModelBase, IFilterViewModel
    {
        private readonly DispatcherTimer _typeTimer;

        protected string _filterText;

        protected bool _isActive;

        protected bool _isBusy;

        protected string _lastFilterText;

        protected Action cancelFilteringAction;

        protected FilterViewModelBase()
        {
            CompletionList = new CompletionListViewModel();
            _filterText = string.Empty;
            _typeTimer = new DispatcherTimer();
            _typeTimer.Tick += OnTypeTimerElapsed;
            _typeTimer.Interval = new TimeSpan(0, 0, 0, 0, 140);
            StartFilterWhenActive = true;
        }

        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }

            protected set
            {
                SetProperty(ref _isBusy, value, "IsBusy");
            }
        }

        public string FilterText
        {
            get
            {
                return _filterText;
            }

            set
            {
                SetProperty(ref _filterText, value, "FilterText", OnFilterTextChanged);
            }
        }

        public CompletionListViewModel CompletionList { get; private set; }

        public bool IsActive
        {
            get
            {
                return _isActive;
            }

            set
            {
                SetProperty(ref _isActive, value, "IsActive", OnIsActiveChanged);
            }
        }

        public virtual void StartFiltering()
        {
            IsBusy = true;
            _lastFilterText = _filterText;
        }

        public virtual void StopFiltering()
        {
            if (_typeTimer.IsEnabled)
            {
                _typeTimer.Stop();
            }

            if (cancelFilteringAction != null && IsBusy)
            {
                cancelFilteringAction();
            }

            IsBusy = false;
        }

        public bool StartFilterWhenActive { get; set; }

        private void OnTypeTimerElapsed(object sender, EventArgs e)
        {
            _typeTimer.Stop();
            StartFiltering();
        }

        protected virtual void OnIsActiveChanged()
        {
            if (!IsActive)
            {
                if (IsBusy)
                {
                    StartFilterWhenActive = true;
                }

                StopFiltering();
            }
            else
            {
                if (_filterText != _lastFilterText)
                {
                    StartFilterWhenActive = true;
                }
            }

            CompletionList.IsActive = IsActive;
        }

        protected virtual void OnFilterTextChanged()
        {
            if (!IsActive)
            {
                return;
            }

            _typeTimer.Stop();
            _typeTimer.Start();
        }

        protected void SelectBestMatchingItem()
        {
            if (CompletionList.FilteredItems.Any())
            {
                var indexOfItemToSelect = 0; // first item

                if (CompletionList.FilteredItems.Count >= 2)
                {
                    var firstNewableItem = CompletionList.FilteredItems[0] as INewable;
                    if (firstNewableItem != null && firstNewableItem.IsNew)
                    {
                        indexOfItemToSelect = 1; // second item
                    }
                }

                CompletionList.SelectedItem = CompletionList.FilteredItems[indexOfItemToSelect];
            }
        }

        protected void SelectBestMatchingItem<T>(Func<T, bool> matchPredicate) where T : INewable
        {
            if (CompletionList.FilteredItems.Count > 0)
            {
                var itemToSlect = CompletionList.FilteredItems.Cast<T>().Where(x => !x.IsNew).FirstOrDefault(matchPredicate);

                if (Equals(itemToSlect, default(T)))
                {
                    itemToSlect = (T)CompletionList.FilteredItems.First();
                }

                CompletionList.SelectedItem = itemToSlect;
            }
        }
    }
}